﻿//////////////////////////////////////////////////
//                 RebornCo.cs                  //
//         Part of RebornCo by kaihaider        //
//////////////////////////////////////////////////

using System.Linq;
using System.Threading.Tasks;
using RebornCo.Rotations;
using RebornCo.Settings;
using System;
using System.Windows.Forms;
using System.Windows.Media;
using ff14bot.Enums;
using ff14bot.NeoProfiles;
using RebornCo.Helpers;
using ff14bot;
using ff14bot.Behavior;
using ff14bot.Helpers;
using ff14bot.Managers;
using ff14bot.Objects;
using ff14bot.AClasses;
//using System.Runtime.InteropServices;
using Extensions = RebornCo.Helpers.Extensions;

[assembly: System.Reflection.AssemblyVersion("0.8.3")]
namespace RebornCo
{
    public abstract partial class RebornCoRoutine : CombatRoutine
    {
        public override float PullRange
        {
            get { return 2.5f; }
        }
        public override string Name { get { return "RebornCo"; } }
        private Form _configForm;
        public static CoreSetting WindowSettings = CoreSetting.Instance;
        public override bool WantButton { get { return true; } }
        public static ClassJobType OldJob { get; set; } 
        
        public override void OnButtonPress()
        {
            if (_configForm == null || _configForm.IsDisposed || _configForm.Disposing)
                _configForm = new SettingsForm();

            _configForm.ShowDialog();
        }
        private Buddy.Coroutines.Coroutine _avoidance;
        public virtual void OnPulse()
        {
            Spell.SyncCombos();
            if (!ShouldPulse) return;
            if (WindowSettings as RogueSetting != null)
            {
                Ninja.NinjutusCooldown = Spell.GetSpellData("Ninjutsu").Cooldown.TotalMilliseconds;
            }
            if (WindowSettings.Dodge)
            {
                if (_avoidance == null || _avoidance.IsFinished)
                {
                    _avoidance = new Buddy.Coroutines.Coroutine(() => Movement.Avoidance());
                }
                _avoidance.Resume();
            }
            if (WantHealing) HealTargeting.Instance.Pulse();
            Extensions.DoubleCastPreventionDict.RemoveAll(t => DateTime.UtcNow.TimeOfDay > t);
            Target.TargetChange();
            Movement.CheckStuckRunning();
            Spell.GcdPulse();
            //Logging.Write(Colors.White, "(RebornCo) Last: " + Spell.LastCombo + " Cache: " + Spell.CachedCombo);
            //Logging.WriteToFileSync(LogLevel.Normal, "(RebornCo) Gcd: " + Spell.Gcd + " " + Spell.GcdTime );
            if (WindowSettings.ShowSpellIds && (Core.Player.CurrentTarget as BattleCharacter) != null && (Core.Player.CurrentTarget as BattleCharacter).IsCasting && (Core.Player.CurrentTarget as BattleCharacter).SpellCastInfo != null)
                Logging.Write(Colors.OrangeRed, "[RebornCo] "+(Core.Player.CurrentTarget as BattleCharacter).SpellCastInfo.Name +" Spell ID " + (Core.Player.CurrentTarget as BattleCharacter).SpellCastInfo.ActionId);
            if (!OldJob.Equals(Core.Player.CurrentJob))
            {
                OldJob = Core.Player.CurrentJob;
                Logging.Write(Colors.OrangeRed, "(RebornCo) Your job has been updated to " + OldJob + ". Rebuilding behaviors...");
                CompositeBuilder._methods.Clear();
                RoutineManager.Reload();
                OnInitialize();
            }
            ShouldPulse = false;
        }

        public static bool WantHealing = false;

        public override sealed void Pulse()
        {
            OnPulse();
        }
        public static bool Resting = false;        

        #region Hidden Overrides

        internal static RebornCoRoutine Instance;

        public override sealed void Initialize()
        {
            OldJob = Core.Player.CurrentJob;
            Logging.Write("Starting " + Name);
            Instance = this;

            Logging.WriteToFileSync(LogLevel.Normal, "(RebornCo) Spell Queue: " + WindowSettings.SpellQueue);

            GameEvents.OnMapChanged += OnGameEventsOnMapChanged;

            TreeHooks.Instance.OnHooksCleared += OnInstanceOnHooksCleared;

            UpdateContext();

            // NOTE: Hook these events AFTER the context update.
            OnGameContextChanged += OnOnGameContextChanged;

            RoutineManager.Reloaded += OnRoutineManagerOnReloaded;

            if (!RebuildBehaviors())
            {
                return;
            }

            Logging.Write(LogLevel.Verbose, Colors.White, "Verified behaviors can be created!");
            OnInitialize();
            if (HotkeyManager.RegisteredHotkeys.Any(r => r.Name.Equals("Dots"))) HotkeyManager.Unregister("Dots");
            if (HotkeyManager.RegisteredHotkeys.Any(r => r.Name.Equals("Jugulate"))) HotkeyManager.Unregister("Jugulate");
            if (HotkeyManager.RegisteredHotkeys.Any(r => r.Name.Equals("DancingEdge"))) HotkeyManager.Unregister("DancingEdge");
            if (HotkeyManager.RegisteredHotkeys.Any(r => r.Name.Equals("StormsPath"))) HotkeyManager.Unregister("StormsPath");
            if (HotkeyManager.RegisteredHotkeys.Any(r => r.Name.Equals("StormsEye"))) HotkeyManager.Unregister("StormsEye");
            if (HotkeyManager.RegisteredHotkeys.Any(r => r.Name.Equals("RotationSwap"))) HotkeyManager.Unregister("RotationSwap");

            WindowSettings.LastHotKeyChoice = WindowSettings.HotKeyChoice;
            WindowSettings.LastModKeyChoice = WindowSettings.ModKeyChoice;
            HotkeyManager.Register("RotationSwap", (Keys)System.Windows.Input.KeyInterop.VirtualKeyFromKey(WindowSettings.HotKeyChoice), (System.Windows.Input.ModifierKeys)WindowSettings.ModKeyChoice, hk => { Spell.RotationSwap = !Spell.RotationSwap; Logging.Write(Spell.RotationSwap ? "[RebornCo] Secondary Rotation Activated!" : "[RebornCo] Primary Rotation Resumed!"); });
            if (WindowSettings as RogueSetting != null)
            {
                (WindowSettings as RogueSetting).LastJugulateHotKeyChoice = (WindowSettings as RogueSetting).JugulateHotKeyChoice;
                (WindowSettings as RogueSetting).LastJugulateModKeyChoice = (WindowSettings as RogueSetting).JugulateModKeyChoice;
                HotkeyManager.Register("Jugulate", (Keys)System.Windows.Input.KeyInterop.VirtualKeyFromKey((WindowSettings as RogueSetting).JugulateHotKeyChoice),
                    (System.Windows.Input.ModifierKeys)(WindowSettings as RogueSetting).JugulateModKeyChoice,
                    hk =>
                    {
                        ((RogueSetting)WindowSettings).JugulateDps = !((RogueSetting)WindowSettings).JugulateDps;
                        Logging.Write(((RogueSetting)WindowSettings).JugulateDps
                            ? "[RebornCo] Jugulate will be used for dps!"
                            : "[RebornCo] Jugulate halted!");
                    });

                (WindowSettings as RogueSetting).LastDancingEdgeHotKeyChoice = (WindowSettings as RogueSetting).DancingEdgeHotKeyChoice;
                (WindowSettings as RogueSetting).LastDancingEdgeModKeyChoice = (WindowSettings as RogueSetting).DancingEdgeModKeyChoice;
                HotkeyManager.Register("DancingEdge", (Keys)System.Windows.Input.KeyInterop.VirtualKeyFromKey((WindowSettings as RogueSetting).DancingEdgeHotKeyChoice),
                    (System.Windows.Input.ModifierKeys)(WindowSettings as RogueSetting).DancingEdgeModKeyChoice,
                    hk =>
                    {
                        Ninja.DancingEdge = !Ninja.DancingEdge;
                        Logging.Write(Ninja.DancingEdge
                            ? "[RebornCo] Dancing Edge will be used!"
                            : "[RebornCo] Dancing Edge combo halted!");
                    });

                (WindowSettings as RogueSetting).LastDotsHotKeyChoice = (WindowSettings as RogueSetting).DotsHotKeyChoice;
                (WindowSettings as RogueSetting).LastDotsModKeyChoice = (WindowSettings as RogueSetting).DotsModKeyChoice;
                HotkeyManager.Register("Dots", (Keys)System.Windows.Input.KeyInterop.VirtualKeyFromKey((WindowSettings as RogueSetting).DotsHotKeyChoice),
                    (System.Windows.Input.ModifierKeys)(WindowSettings as RogueSetting).DotsModKeyChoice,
                    hk =>
                    {
                        Ninja.Dots = !Ninja.Dots;
                        Logging.Write(Ninja.Dots
                            ? "[RebornCo] Dots will be used!"
                            : "[RebornCo] Dots halted!");
                    });
            }
            if (WindowSettings as MarauderSetting != null)
            {
                (WindowSettings as MarauderSetting).LastStormsPathHotKeyChoice = (WindowSettings as MarauderSetting).StormsPathHotKeyChoice;
                (WindowSettings as MarauderSetting).LastStormsPathModKeyChoice = (WindowSettings as MarauderSetting).StormsPathModKeyChoice;
                HotkeyManager.Register("StormsPath", (Keys)System.Windows.Input.KeyInterop.VirtualKeyFromKey((WindowSettings as MarauderSetting).StormsPathHotKeyChoice), 
                    (System.Windows.Input.ModifierKeys)(WindowSettings as MarauderSetting).StormsPathModKeyChoice, 
                    hk => { Marauder.StormsPath = !Marauder.StormsPath; 
                        Logging.Write(Marauder.StormsPath 
                            ? "[RebornCo] Storm's Path will be used!"
                            : "[RebornCo] Storm's Path combo halted!"); });

                (WindowSettings as MarauderSetting).LastStormsEyeHotKeyChoice = (WindowSettings as MarauderSetting).StormsEyeHotKeyChoice;
                (WindowSettings as MarauderSetting).LastStormsEyeModKeyChoice = (WindowSettings as MarauderSetting).StormsEyeModKeyChoice;
                HotkeyManager.Register("StormsEye", (Keys)System.Windows.Input.KeyInterop.VirtualKeyFromKey((WindowSettings as MarauderSetting).StormsEyeHotKeyChoice),
                    (System.Windows.Input.ModifierKeys)(WindowSettings as MarauderSetting).StormsEyeModKeyChoice,
                    hk =>
                    {
                        Marauder.StormsEye = !Marauder.StormsEye;
                        Logging.Write(Marauder.StormsEye
                            ? "[RebornCo] Storm's Eye will be used!"
                            : "[RebornCo] Storm's Eye combo halted!");
                    });
            }
            if (WindowSettings as RogueSetting != null)
            {
                (WindowSettings as RogueSetting).JugulateSpells = (WindowSettings as RogueSetting).JugulateSpells.Replace(" ", string.Empty).ToLower();
                (WindowSettings as RogueSetting).JugulateList = (WindowSettings as RogueSetting).JugulateSpells.Split(',').ToList();
                (WindowSettings as RogueSetting).ShurikenMobs = (WindowSettings as RogueSetting).ShurikenMobs.Replace(" ", string.Empty).ToLower();
                (WindowSettings as RogueSetting).ShurikenList = (WindowSettings as RogueSetting).ShurikenMobs.Split(',').ToList();
            }
            if (WindowSettings as WhiteMageSetting != null)
            {
                (WindowSettings as WhiteMageSetting).EsunaSpells = (WindowSettings as WhiteMageSetting).EsunaSpells.Replace(" ", string.Empty).ToLower();
                (WindowSettings as WhiteMageSetting).EsunaList = (WindowSettings as WhiteMageSetting).EsunaSpells.Split(',').ToList();
            }
            var pot = InventoryManager.FilledSlots.FirstOrDefault(a => a.Name.Equals(WindowSettings.PotionName));
            if (pot != null && pot.IsValid)
            {
                Spell.Potion = pot;
                Logging.Write("[RebornCo] Found: " + pot.Name);
            }
            Logging.Write("Initialization complete!");
        }

        public abstract void OnInitialize();

        private void OnInstanceOnHooksCleared(object s, EventArgs e)
        {
            Logging.Write(Colors.White, "Hooks cleared, re-creating behaviors");
            RebuildBehaviors(true);
        }

        private void OnGameEventsOnMapChanged(object s, EventArgs e)
        {
            UpdateContext();
        }

        private void OnRoutineManagerOnReloaded(object s, EventArgs e)
        {
            Logging.Write(Colors.White, "Routines were reloaded, re-creating behaviors");
            RebuildBehaviors(true);
        }

        private void OnOnGameContextChanged(object orig, GameContextEventArg ne)
        {
            Logging.Write(Colors.White, "Context changed, re-creating behaviors");
            RebuildBehaviors();
        }

        public override void ShutDown()
        {
            GameEvents.OnMapChanged -= OnGameEventsOnMapChanged;
            TreeHooks.Instance.OnHooksCleared -= OnInstanceOnHooksCleared;
            OnGameContextChanged -= OnOnGameContextChanged;
            RoutineManager.Reloaded -= OnRoutineManagerOnReloaded;
            _lastContext = GameContext.None;
            CompositeBuilder._methods.Clear();
        }
        #endregion

        public static bool IsValid()
        {
            return Core.Player.IsValid && WindowSettings.Combat && !Core.Player.IsMounted && Core.IsInGame && !Core.Player.IsDead
                && !QuestLogManager.InCutscene && !ff14bot.RemoteWindows.Talk.DialogOpen;
        }
        public static bool ShouldPulse;
#pragma warning disable 1998
        public static async Task<bool> DefaultRestBehavior(float currentEnergy)
#pragma warning restore 1998
        {
            SpellData sprint;
            var sprintCooldown = !Actionmanager.CurrentActions.TryGetValue("Sprint", out sprint) ? 0 : sprint.Cooldown.TotalMilliseconds;
            if (Core.Player.CurrentHealthPercent < WindowSettings.RestHealthDone ||
                currentEnergy < WindowSettings.RestEnergyDone && Math.Abs(sprintCooldown) < 50)
            {
                Logging.Write(Colors.OrangeRed, "[RebornCo] Resting");
                if (MovementManager.IsMoving) Movement.StopMove();
                Resting = true;
                return true;
            }
            Resting = false;
            return false;
        }

// ReSharper disable once CSharpWarnings::CS1998
        public static async Task<bool> CombatCompanion()
        {
            if (Core.Player.IsCasting && (Core.Player.SpellCastInfo == null || Core.Player.SpellCastInfo.ActionType == ActionType.Item || Core.Player.SpellCastInfo.ActionType == ActionType.Companion)) return true;
            if (!WindowSettings.CombatChocobo || Chocobo.Summoned || !Chocobo.CanSummon
                || WindowSettings.CombatChocoboExtraChecks && PartyManager.IsInParty && PartyManager.VisibleMembers.Any(bird => bird.GameObject.SummonerObjectId == Core.Player.ObjectId 
                    && bird.GameObject.NpcId == 0 && bird.GameObject.Type == GameObjectType.BattleNpc)) return false;
            if (!MovementManager.IsMoving) return SummonChocobo();
            Movement.StopMove();
            SummonChocobo();
            if (await Buddy.Coroutines.Coroutine.Wait(1500, () => Core.Player.IsCasting))
            {
                return await Buddy.Coroutines.Coroutine.Wait(1300, () => Chocobo.Summoned);
            }
            return false;
        }

        private static bool SummonChocobo()
        {
            Chocobo.Summon();
            return false;
        }
    }
}